package com.gitee.wsl.concurrent.collections


import kotlinx.atomicfu.locks.SynchronizedObject
import kotlinx.atomicfu.locks.synchronized
import kotlin.jvm.JvmName

class ConcurrentMutableMap<K, V> internal constructor(
    rootArg: SynchronizedObject? = null,
    private val del: MutableMap<K, V>
) : SynchronizedObject(), MutableMap<K, V> {

    constructor() : this(null, mutableMapOf())

    private val syncTarget: SynchronizedObject = rootArg ?: this

    override val size: Int
        get() = synchronized(syncTarget)  { del.size }
    override val entries: MutableSet<MutableMap.MutableEntry<K, V>>
        get() = synchronized(syncTarget)  { ConcurrentMutableSet(this, del.entries) }
    override val keys: MutableSet<K>
        get() = synchronized(syncTarget)  { ConcurrentMutableSet(this, del.keys) }
    override val values: MutableCollection<V>
        get() = synchronized(syncTarget)  { ConcurrentMutableCollection(this, del.values) }

    override fun containsKey(key: K): Boolean = synchronized(syncTarget)  { del.containsKey(key) }
    override fun containsValue(value: V): Boolean = synchronized(syncTarget)  { del.containsValue(value) }
    override fun get(key: K): V? = synchronized(syncTarget)  { del.get(key) }
    override fun isEmpty(): Boolean = synchronized(syncTarget)  { del.isEmpty() }
    override fun clear() {
        synchronized(syncTarget)  { del.clear() }
    }

    /**
     * If the specified key is not already associated with a value
     * attempts to compute its value using the given mapping function and enters it into this map
     */
    @JvmName("safeComputeIfAbsent")
    fun computeIfAbsent(key: K, defaultValue: (K) -> V): V {
        return synchronized(syncTarget)  {
            val value = del[key]
            if (value == null) {
                val newValue = defaultValue(key)
                del[key] = newValue
                newValue
            } else {
                value
            }
        }
    }

    override fun put(key: K, value: V): V? = synchronized(syncTarget)  { del.put(key, value) }

    override fun putAll(from: Map<out K, V>) {
        synchronized(syncTarget)  { del.putAll(from) }
    }

    override fun remove(key: K): V? = synchronized(syncTarget)  { del.remove(key) }

    fun <R> block(f: (MutableMap<K, V>) -> R): R = synchronized(syncTarget)  {
        val wrapper = MutableMapWrapper(del)
        val result = f(wrapper)
        wrapper.map = mutableMapOf()
        result
    }

    fun putIfAbsent(key: K, item: V): V? {
        return synchronized(syncTarget)  {
            val value = del[key]
            if (value == null) {
                del[key] = item
                null
            } else {
                value
            }
        }
    }
}

internal class ConcurrentMutableListIterator<E>(
    private val root: SynchronizedObject,
    private val del: MutableListIterator<E>
) : ConcurrentMutableIterator<E>(root, del),
    MutableListIterator<E> {
    override fun hasPrevious(): Boolean = synchronized(root)  { del.hasPrevious() }

    override fun nextIndex(): Int = synchronized(root)  { del.nextIndex() }

    override fun previous(): E = synchronized(root)  { del.previous() }

    override fun previousIndex(): Int = synchronized(root)  { del.previousIndex() }

    override fun add(element: E) {
        synchronized(root)  { del.add(element) }
    }

    override fun set(element: E) {
        synchronized(root)  { del.set(element) }
    }
}

internal class MutableMapWrapper<K, V>(internal var map: MutableMap<K, V>) : MutableMap<K, V> {
    override val size: Int
        get() = map.size

    override fun containsKey(key: K): Boolean = map.containsKey(key)

    override fun containsValue(value: V): Boolean = map.containsValue(value)

    override fun get(key: K): V? = map.get(key)

    override fun isEmpty(): Boolean = map.isEmpty()

    override val entries: MutableSet<MutableMap.MutableEntry<K, V>>
        get() = map.entries
    override val keys: MutableSet<K>
        get() = map.keys
    override val values: MutableCollection<V>
        get() = map.values

    override fun clear() {
        map.clear()
    }

    override fun put(key: K, value: V): V? = map.put(key, value)

    override fun putAll(from: Map<out K, V>) {
        map.putAll(from)
    }

    override fun remove(key: K): V? = map.remove(key)
}